package com.jusfoun.communication.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.jusfoun.communication.constant.MessageConstant;
import com.jusfoun.communication.dao.AppInfoDao;
import com.jusfoun.communication.dao.MessageDao;
import com.jusfoun.communication.dao.UserDao;
import com.jusfoun.communication.model.AutoReply;
import com.jusfoun.communication.model.Message;
import com.jusfoun.communication.util.PageInfo;
import com.jusfoun.communication.util.Result;
import com.jusfoun.communication.util.ResultUtil;
import com.jusfoun.communication.websocket.WebSocketEndPoint;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * websocket消息处理类
 *
 * @author player six
 * @email jslcylcy@163.com
 * @create 2017-10-12 17:54
 **/
@Service
public class MessageService {

    private static final Logger logger = LoggerFactory.getLogger(MessageService.class);

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ResultUtil resultUtil;

    @Autowired
    private WebSocketEndPoint webSocketEndPoint;

    @Autowired
    private UserService userService;

    @Autowired
    private AutoReplyService autoReplyService;

    @Autowired
    private MessageDao messageDao;

    @Autowired
    private AppInfoDao appInfoDao;

    @Autowired
    private UserDao userDao;

    /**
     * 处理websocket消息
     *
     * @param session
     * @param appId
     * @param userName
     * @param message
     */
    public void handleMessage(WebSocketSession session, String appId, String userName, String message) throws IOException {
        Message receive = null;
        try {
            receive = objectMapper.readValue(message, Message.class);
        } catch (IOException e) {
            //发送消息格式错误
            logger.error("接收websocket消息出错", e);
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(new Message(MessageConstant.CODE_INVALID_DATA))));
            return;
        }

        if (MessageConstant.CODE_COMMON == receive.getCode() || MessageConstant.CODE_COMMON_AUTOREPLY == receive.getCode()) {
            //普通消息
            if (StringUtils.isBlank(receive.getTo())) {
                //发送消息缺少发送对象
                session.sendMessage(new TextMessage(objectMapper.writeValueAsString(new Message(MessageConstant.CODE_INVALID_DATA_NOTO))));
                return;
            }
            receive.setFrom(userName);
            receive.setAppId(appId);
            saveCommonMsg4Send(receive);
        } else if (MessageConstant.CODE_HEART == receive.getCode()) {
            //心跳消息
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(new Message(MessageConstant.CODE_HEART))));
        } else {
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(new Message(MessageConstant.CODE_INVALID_CODE))));
        }
    }

    /**
     * 保存待发送消息(保证消息格式正确)
     * 普通消息(用户之间的一对一消息)
     *
     * @param message
     */
    private void saveCommonMsg4Send(Message message) {
        message.setTime(new Date());
        messageDao.add(message);
        if (MessageConstant.CODE_COMMON == message.getCode() || MessageConstant.CODE_COMMON_AUTOREPLY == message.getCode()) {
            messageDao.addUserMessageCommon(message);
            //普通消息需要校验是否需要自动回复
            if (MessageConstant.CODE_COMMON_AUTOREPLY == message.getCode()) {
                addAutoReply(message);
            }
        }
        if (MessageConstant.CODE_SYSTEM_SPECIAL == message.getCode() || MessageConstant.CODE_SYSTEM_ALL == message.getCode() || MessageConstant.CODE_SYSTEM_GROUP == message.getCode()
                || MessageConstant.CODE_SYSTEM_TAGS == message.getCode()) {
            messageDao.addUserMessageSystem(message);
        }
        //向消息队列推送消息
        messageDao.push4Send(message);
    }

    /**
     * 自动回复
     *
     * @param message
     */
    private void addAutoReply(Message message) {
        List<AutoReply> autoReplyList = autoReplyService.listAutoReplyByAppIdAndTime(message.getAppId(), new Date());
        if (autoReplyList == null || autoReplyList.isEmpty()) {
            return;
        }
        for (AutoReply autoReply : autoReplyList) {
            if (Pattern.matches(autoReply.getPattern(), message.getText())) {
                //匹配自动回复
                Message autoReplyMsg = new Message();
                autoReplyMsg.setTo(message.getFrom());
                autoReplyMsg.setStatus(MessageConstant.STATUS_NOT_SEND);
                autoReplyMsg.setAppId(message.getAppId());
                autoReplyMsg.setText(autoReply.getContent());
                autoReplyMsg.setTime(new Date());
                autoReplyMsg.setCode(MessageConstant.CODE_AUTOREPLY);
                messageDao.add(autoReplyMsg);
                messageDao.addUserMessageSystem(autoReplyMsg);
                messageDao.push4Send(autoReplyMsg);
                return;
            }
        }
    }

    /**
     * 系统消息,给所有用户发送
     *
     * @param message
     */
    private void saveSystemMsg4Send(Message message) {
        Set<String> users = new HashSet<>();
        //获取所有的用户
        if (MessageConstant.CODE_SYSTEM_ALL == message.getCode()) {
            users.addAll(userDao.listAllUserName(message.getAppId()));
        } else if (MessageConstant.CODE_SYSTEM_GROUP == message.getCode()) {
            users.addAll(userDao.listGroupUser(message.getAppId(), message.getTo()));
        } else if (MessageConstant.CODE_SYSTEM_TAGS == message.getCode()) {
            String[] tags = userService.getValidTags(message.getTo());
            for (String tag : tags) {
                users.addAll(userDao.listTagUser(message.getAppId(), tag));
            }
        }
        for (String user : users) {
            message.setTo(user);
            saveCommonMsg4Send(message);
        }
    }

    private void sendMsgById(Long id) {
        logger.debug("处理消息:" + id);
        Message message = messageDao.getById(id);
        if (null != message) {
            if (webSocketEndPoint.sendMsg(message)) {
                message.setStatus(MessageConstant.STATUS_SENDED);
                messageDao.update(message);
            } else {
                messageDao.push4OffLine(message);
            }
        }
    }

    /**
     * 发送用户离线消息
     *
     * @param appId
     * @param userName
     */
    public void sendOffLineMsg(String appId, String userName) {
        List<Long> idList = messageDao.getOfflineIdList(appId, userName);
        for (Long id : idList) {
            sendMsgById(id);
        }
    }

    /**
     * 通过api向用户发送消息
     *
     * @param message
     * @return
     */
    public Result dealSendMsg(Message message) {
        if (MessageConstant.CODE_COMMON == message.getCode() || MessageConstant.CODE_COMMON_AUTOREPLY == message.getCode()) {
            //普通消息
            if (StringUtils.isBlank(message.getTo()) || StringUtils.isBlank(message.getFrom())) {
                //消息格式错误
                return resultUtil.fail("失败", "消息发送方与接收方不能为空");
            }
            saveCommonMsg4Send(message);
            return resultUtil.success();
        } else if (MessageConstant.CODE_SYSTEM_SPECIAL == message.getCode()) {
            //系统给用户的推送消息
            if (StringUtils.isBlank(message.getTo())) {
                return resultUtil.fail("失败", "接收方不能为空");
            }
            saveCommonMsg4Send(message);
            return resultUtil.success();
        } else if (MessageConstant.CODE_SYSTEM_ALL == message.getCode()) {
            //系统推送给所有用户
            saveSystemMsg4Send(message);
            return resultUtil.success();
        } else if (MessageConstant.CODE_SYSTEM_GROUP == message.getCode()) {
            if (StringUtils.isBlank(message.getTo())) {
                return resultUtil.fail("失败", "接收group不能为空");
            }
            saveSystemMsg4Send(message);
            return resultUtil.success();
        } else if (MessageConstant.CODE_SYSTEM_TAGS == message.getCode()) {
            if (StringUtils.isBlank(message.getTo())) {
                return resultUtil.fail("失败", "接收tags不能为空");
            }
            saveSystemMsg4Send(message);
            return resultUtil.success();
        }

        return resultUtil.fail("失败", "非法code");
    }

    /**
     * 读取待发送的消息并发送
     */
    public void dealMsg2Send() {
        try {
            logger.info("监听消息队列");
            Long id = messageDao.getMsgId4Send();
            logger.info("处理消息,id:" + id);
            if (null != id) {
                sendMsgById(id);
            }
        } catch (Exception e) {
            logger.error("处理待发送消息队列异常", e);
        }
    }

    /**
     * 查询两个用户之间的历史消息
     *
     * @param message
     * @param pageNum
     * @param pageSize
     * @return
     */
    public PageInfo<Message> historyMsg(Message message, Integer pageNum, Integer pageSize, Boolean scanIndexForward) {
        if (appInfoDao.checkAppIdAndSecret(message.getAppId(), message.getAppSecret())) {
            long size = messageDao.listHistoryMsgCount(message.getAppId(), message.getTo(), message.getFrom());
            if (size > 0) {
                PageInfo<Message> messagePageInfo = new PageInfo<>(pageNum, pageSize, size);
                List<String> idList = messageDao.listHistoryMsgId(message.getAppId(), message.getTo(), message.getFrom(), messagePageInfo, scanIndexForward);
                List<Message> messageList = getMessageByIdList(idList);
                messagePageInfo.setList(messageList);
                messagePageInfo.calSize();
                return messagePageInfo;
            } else {
                logger.info(message.getTo() + "," + message.getFrom() + ",之间没有在线交流记录");
            }
        } else {
            logger.info("appId与appSecret不匹配");
        }
        return new PageInfo<>();
    }

    /**
     * 根据信息返回用户消息列表
     *
     * @param appId
     * @param userName
     * @param page
     * @param rows
     * @param scanIndexForward
     * @param beginTime
     * @param endTime
     * @return
     */
    public PageInfo<Message> listMessage(String appId, String userName, Integer page, Integer rows, Boolean scanIndexForward,
                                         String beginTime, String endTime) {
        try {
            long min = 0;
            long max = Long.MAX_VALUE;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            if (StringUtils.isNotBlank(beginTime)) {
                min = sdf.parse(beginTime).getTime();
            }
            if (StringUtils.isNotBlank(endTime)) {
                max = sdf.parse(endTime).getTime();
            }


            long size = messageDao.listHistoryMsgCount(appId, userName, min, max);
            if (size > 0) {
                PageInfo<Message> messagePageInfo = new PageInfo<>(page, rows, size);
                List<String> idList = messageDao.listHistoryMsgId(appId, userName, messagePageInfo, scanIndexForward, min, max);
                List<Message> messageList = getMessageByIdList(idList);
                messagePageInfo.setList(messageList);
                messagePageInfo.calSize();
                return messagePageInfo;
            } else {
                logger.info("appId: " + appId + ",userName:" + ",没有消息");
            }
        } catch (ParseException e) {
            logger.error("解析时间出错", e);
        }

        return new PageInfo<>();
    }

    private List<Message> getMessageByIdList(List<String> idList) {
        List<Message> messageList = new ArrayList<>();
        Message msg;
        for (String id : idList) {
            if (StringUtils.isNotBlank(id)) {
                msg = messageDao.getById(Long.valueOf(id));
                if (null != msg) {
                    messageList.add(msg);
                }
            }
        }
        return messageList;
    }

    public void dealLoginMsg(String appId, String userName) {
        messageDao.dealLoginMsg(appId, userName);
    }

    public void dealLogoutMsg(String appId, String userName) {
        messageDao.dealLogoutMsg(appId, userName);
    }
}
